home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NeXTSTEP 3.3 (Developer)…68k, x86, SPARC, PA-RISC]
/
NeXTSTEP 3.3 Dev Intel.iso
/
NextDeveloper
/
Source
/
GNU
/
cctools
/
as
/
sparc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-22
|
31KB
|
1,394 lines
/* tc-sparc.c -- Assemble for the SPARC
Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* relocation type for internal assembler use only */
#define SPARC_RELOC_13 (127)
#define SPARC_RELOC_22 (126)
#define cypress 1234
#undef DEBUGINSN
#include <stdio.h>
#include <ctype.h>
#include "as.h"
#include "libc.h"
#include "md.h"
#include "messages.h"
#include "symbols.h"
/* careful, this file includes data *declarations* */
#include "sparc-opcode.h"
#include <mach-o/sparc/reloc.h>
/* From GNU ansidecl.h */
#define PARAMS(paramlist) paramlist
typedef long offsetT;
/*
* These are the default cputype and cpusubtype for the Sparc architecture.
*/
const cpu_type_t md_cputype = CPU_TYPE_SPARC;
cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_SPARC_ALL;
/* This is the byte sex for the Sparc architecture */
const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX;
/* These characters start a comment anywhere on the line */
const char md_comment_chars[] = ";!";
/* These characters only start a comment at the beginning of a line */
const char md_line_comment_chars[] = "#";
/*
* These characters can be used to separate mantissa decimal digits from
* exponent decimal digits in floating point numbers.
*/
const char md_EXP_CHARS[] = "eE";
/*
* The characters after a leading 0 that means this number is a floating point
* constant as in 0f123.456 or 0d1.234E-12 (see md_EXP_CHARS above).
*/
const char md_FLT_CHARS[] = "dDfF";
static void sparc_ip PARAMS ((char *));
static enum sparc_architecture current_architecture = v6;
static int architecture_requested;
static int warn_on_bump;
const relax_typeS md_relax_table[1];
/* handle of the OPCODE hash table */
static struct hash_control *op_hash = NULL;
#ifdef NeXT
static void s_proc PARAMS ((int));
static void s_ignore PARAMS ((int));
extern void s_seg PARAMS ((int));
#else NeXT
static void s_data1 PARAMS ((void));
static void s_seg PARAMS ((int));
static void s_proc PARAMS ((int));
static void s_reserve PARAMS ((int));
static void s_common PARAMS ((int));
#endif NeXT
const pseudo_typeS md_pseudo_table[] =
{
#ifdef NeXT
{"global", s_globl, 0}, /* Maybe we should fix compiler to use globl */
{"proc", s_proc, 0}, /* nop??? */
/* This are to handle SUN assembler files and point to existing handlers */
{"empty", s_ignore, 0},
{"ident", s_ignore, 0},
{"optim", s_ignore, 0},
{"skip", s_space, 0},
{"type", s_ignore, 0},
{"word", cons, 4},
{"half", cons, 2},
/* these are custom handlers for SUN SPARC assembler only */
#else NeXT
{"seg", s_seg, 0},
{"align", s_align_bytes, 0}, /* Defaulting is invalid (0) */
{"common", s_common, 0},
{"global", s_globl, 0},
{"half", cons, 2},
{"optim", s_ignore, 0},
{"proc", s_proc, 0},
{"reserve", s_reserve, 0},
{"seg", s_seg, 0},
{"skip", s_space, 0},
{"word", cons, 4},
#endif NeXT
{NULL, 0, 0},
};
const int md_short_jump_size = 4;
const int md_long_jump_size = 4;
const int md_reloc_size = 12; /* Size of relocation record */
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful */
const char comment_chars[] = "!"; /* JF removed '|' from comment_chars */
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
.line and .file directives will appear in the pre-processed output */
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
/* Also note that comments started like this one will always
work if '/' isn't otherwise defined. */
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "";
/* Chars that can be used to separate mant from exp in floating point nums */
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant */
/* As in 0f12.456 */
/* or 0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXpP";
/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
changed in read.c. Ideally it shouldn't have to know about it at all,
but nothing is ideal around here. */
static unsigned char octal[256];
#define isoctal(c) octal[(unsigned char) (c)]
static unsigned char toHex[256];
struct sparc_it
{
char *error;
unsigned long opcode;
struct nlist *nlistp;
expressionS exp;
int pcrel;
char pcrel_reloc; /* do relocation? */
enum reloc_type_sparc reloc;
};
struct sparc_it the_insn;
#ifdef DEBUGINSN
static void print_insn PARAMS ((struct sparc_it *insn));
#endif
static int getExpression PARAMS ((char *str));
static char *expr_end;
/*
* Indicates a 'set' instruction which may require a either
* of the following instructions depending on the size of the
* value argument:
*
* sethi %hi(value),reg
*
* or %g0,value,reg
*
* sethi %hi(value),reg
* or reg,%lo(value),reg
*
*/
static int special_case_set = 0;
/* s_proc and s_ignore are included for rudimentary
compatibility with the Sun assembler only */
static void
s_proc (ignore)
int ignore;
{
totally_ignore_line();
}
/* we simply ignore the rest of this statement */
static void
s_ignore (ignore)
int ignore;
{
totally_ignore_line();
}
/* This function is called once, at assembler startup time. It should
set up all the tables, etc. that the MD part of the assembler will need. */
void
md_begin ()
{
register const char *retval = NULL;
int lose = 0;
register unsigned int i = 0;
op_hash = hash_new ();
while (i < NUMOPCODES)
{
const char *name = sparc_opcodes[i].name;
retval = hash_insert (op_hash, (char *)name, (char *)&sparc_opcodes[i]);
if(retval != NULL && *retval != '\0') {
fprintf (stderr, "internal error: can't hash `%s': %s\n",
sparc_opcodes[i].name, retval);
lose = 1;
} do {
if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
{
fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n",
sparc_opcodes[i].name, sparc_opcodes[i].args);
lose = 1;
}
++i;
}
while (i < NUMOPCODES
&& !strcmp (sparc_opcodes[i].name, name));
}
if (lose)
as_fatal ("Broken assembler. No assembly attempted.");
for (i = '0'; i < '8'; ++i)
octal[i] = 1;
for (i = '0'; i <= '9'; ++i)
toHex[i] = i - '0';
for (i = 'a'; i <= 'f'; ++i)
toHex[i] = i + 10 - 'a';
for (i = 'A'; i <= 'F'; ++i)
toHex[i] = i + 10 - 'A';
}
void
md_end(
void)
{
return;
}
void
md_assemble (str)
char *str;
{
char *toP;
int rsd;
know (str);
sparc_ip (str);
#ifdef DEBUGINSN
print_insn(&the_insn);
#endif
/* See if "set" operand is absolute and small; skip sethi if so. */
if (special_case_set && the_insn.exp.X_seg == SEG_ABSOLUTE)
{
if (the_insn.exp.X_add_number >= -(1 << 12)
&& the_insn.exp.X_add_number < (1 << 12))
{
the_insn.opcode = 0x80102000 /* or %g0,imm,... */
| (the_insn.opcode & 0x3E000000) /* dest reg */
| (the_insn.exp.X_add_number & 0x1FFF); /* imm */
special_case_set = 0; /* No longer special */
the_insn.reloc = SPARC_RELOC_NONE; /* No longer relocated */
}
}
toP = frag_more (4);
/* put out the opcode */
md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
/* put out the symbol-dependent stuff */
if (the_insn.reloc != SPARC_RELOC_NONE)
{
fix_new(frag_now,
(toP - frag_now->fr_literal),
4,
the_insn.exp.X_add_symbol,
the_insn.exp.X_subtract_symbol,
the_insn.exp.X_add_number,
the_insn.pcrel,
the_insn.pcrel_reloc, /* 1 for local labels due to scatter loading */
the_insn.reloc);
}
if (special_case_set) {
special_case_set = 0;
assert (the_insn.reloc == SPARC_RELOC_HI22);
/* See if "set" operand has no low-order bits; skip OR if so. */
if ((the_insn.exp.X_seg == SEG_ABSOLUTE) &&
((the_insn.exp.X_add_number & 0x3FF) == 0))
return;
toP = frag_more (4);
rsd = (the_insn.opcode >> 25) & 0x1f;
the_insn.opcode = 0x80102000 | (rsd << 25) | (rsd << 14);
md_number_to_chars (toP, (valueT) the_insn.opcode, 4);
the_insn.pcrel_reloc = 0;
fix_new(frag_now,
(toP - frag_now->fr_literal),
4,
the_insn.exp.X_add_symbol,
the_insn.exp.X_subtract_symbol,
the_insn.exp.X_add_number,
the_insn.pcrel,
the_insn.pcrel_reloc,
SPARC_RELOC_LO10);
return;
}
}
static void
sparc_ip (str)
char *str;
{
char *error_message = "";
char *s;
const char *args;
char c;
struct sparc_opcode *insn;
char *argsStart;
unsigned long opcode;
unsigned int mask = 0;
int match = 0;
int comma = 0;
long immediate_max = 0;
for (s = str; islower (*s) || (*s >= '0' && *s <= '3'); ++s)
;
switch (*s)
{
case '\0':
break;
case ',':
comma = 1;
/*FALLTHROUGH */
case ' ':
*s++ = '\0';
break;
default:
as_bad ("Unknown opcode: `%s'", str);
exit (1);
}
if ((insn = (struct sparc_opcode *) hash_find (op_hash, str)) == NULL)
{
as_bad ("Unknown opcode: `%s'", str);
return;
}
if (comma)
{
*--s = ',';
}
argsStart = s;
for (;;)
{
opcode = insn->match;
memset (&the_insn, '\0', sizeof (the_insn));
the_insn.reloc = SPARC_RELOC_NONE;
the_insn.pcrel_reloc = 1; /* default, reloc, for scatter loading */
/*
* Build the opcode, checking as we go to make
* sure that the operands match
*/
for (args = insn->args;; ++args)
{
switch (*args)
{
case 'M':
case 'm':
if (strncmp (s, "%asr", 4) == 0)
{
s += 4;
if (isdigit (*s))
{
long num = 0;
while (isdigit (*s))
{
num = num * 10 + *s - '0';
++s;
}
if (num < 16 || 31 < num)
{
error_message = ": asr number must be between 15 and 31";
goto error;
} /* out of range */
opcode |= (*args == 'M' ? RS1 (num) : RD (num));
continue;
}
else
{
error_message = ": expecting %asrN";
goto error;
} /* if %asr followed by a number. */
} /* if %asr */
break;
case '\0': /* end of args */
if (*s == '\0')
{
match = 1;
}
break;
case '+':
if (*s == '+')
{
++s;
continue;
}
if (*s == '-')
{
continue;
}
break;
case '[': /* these must match exactly */
case ']':
case ',':
case ' ':
if (*s++ == *args)
continue;
break;
case '#': /* must be at least one digit */
if (isdigit (*s++))
{
while (isdigit (*s))
{
++s;
}
continue;
}
break;
case 'C': /* coprocessor state register */
if (strncmp (s, "%csr", 4) == 0)
{
s += 4;
continue;
}
break;
case 'b': /* next operand is a coprocessor register */
case 'c':
case 'D':
if (*s++ == '%' && *s++ == 'c' && isdigit (*s))
{
mask = *s++;
if (isdigit (*s))
{
mask = 10 * (mask - '0') + (*s++ - '0');
if (mask >= 32)
{
break;
}
}
else
{
mask -= '0';
}
switch (*args)
{
case 'b':
opcode |= mask << 14;
continue;
case 'c':
opcode |= mask;
continue;
case 'D':
opcode |= mask << 25;
continue;
}
}
break;
case 'r': /* next operand must be a register */
case 'u':
case '1':
case '2':
case 'd':
if (*s++ == '%')
{
switch (c = *s++)
{
case 'f': /* frame pointer */
if (*s++ == 'p')
{
mask = 0x1e;
break;
}
error_message = ": register not fp";
goto error;
case 'g': /* global register */
if (isoctal (c = *s++))
{
mask = c - '0';
break;
}
error_message = ": invalid global register";
goto error;
case 'i': /* in register */
if (isoctal (c = *s++))
{
mask = c - '0' + 24;
break;
}
error_message = ": invalid in register";
goto error;
case 'l': /* local register */
if (isoctal (c = *s++))
{
mask = (c - '0' + 16);
break;
}
error_message = ": invalid local register";
goto error;
case 'o': /* out register */
if (isoctal (c = *s++))
{
mask = (c - '0' + 8);
break;
}
error_message = ": invalid out register";
goto error;
case 's': /* stack pointer */
if (*s++ == 'p')
{
mask = 0xe;
break;
}
error_message = ": register is not sp";
goto error;
case 'r': /* any register */
if (!isdigit (c = *s++))
{
error_message = ": invalid register";
goto error;
}
/* FALLTHROUGH */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (isdigit (*s))
{
if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
{
error_message = ": register # out of range";
goto error;
}
}
else
{
c -= '0';
}
mask = c;
break;
default:
error_message = ": invalid resgiter #";
goto error;
}
/*
* Got the register, now figure out where
* it goes in the opcode.
*/
switch (*args)
{
case '1':
opcode |= mask << 14;
continue;
case '2':
opcode |= mask;
continue;
case 'd':
opcode |= mask << 25;
continue;
case 'r':
opcode |= (mask << 25) | (mask << 14);
continue;
case 'u':
opcode |= (mask << 25) | mask;
continue;
}
}
break;
case 'e': /* next operand is a floating point register */
case 'v':
case 'V':
case 'f':
case 'B':
case 'R':
case 'g':
case 'H':
case 'J':
{
char format;
if (*s++ == '%'
&& ((format = *s) == 'f')
&& isdigit (*++s))
{
for (mask = 0; isdigit (*s); ++s)
{
mask = 10 * mask + (*s - '0');
} /* read the number */
if ((*args == 'v'
|| *args == 'B'
|| *args == 'H')
&& (mask & 1))
{
break;
} /* register must be even numbered */
if ((*args == 'V'
|| *args == 'R'
|| *args == 'J')
&& (mask & 3))
{
break;
} /* register must be multiple of 4 */
if (mask >= 32)
{
error_message = ": There are only 32 f registers; [0-31]";
goto error;
} /* on error */
}
else
{
break;
} /* if not an 'f' register. */
switch (*args)
{
case 'v':
case 'V':
case 'e':
opcode |= RS1 (mask);
continue;
case 'f':
case 'B':
case 'R':
opcode |= RS2 (mask);
continue;
case 'g':
case 'H':
case 'J':
opcode |= RD (mask);
continue;
} /* pack it in. */
know (0);
break;
} /* float arg */
case 'F':
if (strncmp (s, "%fsr", 4) == 0)
{
s += 4;
continue;
}
break;
case 'h': /* high 22 bits */
the_insn.reloc = SPARC_RELOC_HI22;
goto immediate;
case 'l': /* 22 bit PC relative immediate */
the_insn.reloc = SPARC_RELOC_WDISP22;
the_insn.pcrel = 1;
goto immediate;
case 'L': /* 30 bit immediate for call insn */
the_insn.reloc = SPARC_RELOC_WDISP30;
the_insn.pcrel = 1;
goto immediate;
case 'n': /* 22 bit immediate */
the_insn.reloc = SPARC_RELOC_22;
goto immediate;
case 'i': /* 13 bit immediate */
/* What's the difference between base13 and 13?
13-bit immediate and 13-bit immediate+register */
the_insn.reloc = SPARC_RELOC_13;
immediate_max = 0x1FFF;
/*FALLTHROUGH */
immediate:
if (*s == ' ')
s++;
if (*s == '%')
{
if ((c = s[1]) == 'h' && s[2] == 'i')
{
the_insn.reloc = SPARC_RELOC_HI22;
s += 3;
}
else if (c == 'l' && s[2] == 'o')
{
the_insn.reloc = SPARC_RELOC_LO10;
s += 3;
}
else
break;
}
/* Note that if the getExpression() fails, we will still
have created U entries in the symbol table for the
'symbols' in the input string. Try not to create U
symbols for registers, etc. */
{
/* This stuff checks to see if the expression ends in
+%reg. If it does, it removes the register from
the expression, and re-sets 's' to point to the
right place. */
char *s1;
for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++);
if (s1 != s && isdigit (s1[-1]))
{
if (s1[-2] == '%' && s1[-3] == '+')
{
s1 -= 3;
*s1 = '\0';
(void) getExpression (s);
*s1 = '+';
s = s1;
continue;
}
else if (strchr ("goli0123456789",
s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
{
s1 -= 4;
*s1 = '\0';
(void) getExpression (s);
*s1 = '+';
s = s1;
continue;
}
}
}
(void) getExpression (s);
s = expr_end;
/* The Next linker has the ability to scatter blocks of sections between
* labels. This requires that branches to labels that survive to the
* link phase be relocatable. These labels are those that are not L*
*/
if (the_insn.exp.X_add_symbol != NULL && !flagseen['L']
&& the_insn.exp.X_add_symbol->sy_nlist.n_un.n_name[0] == 'L') {
/* local symbol which will be thrown away. Don't bother
* to reloc it.
*/
the_insn.pcrel_reloc = 0;
}
if ((the_insn.exp.X_seg == SEG_ABSOLUTE ||
the_insn.exp.X_seg == SEG_BIG)
&& the_insn.exp.X_add_symbol == 0)
{
/* Check for invalid constant values. Don't warn if
constant was inside %hi or %lo, since these
truncate the constant to fit. */
if (immediate_max != 0
&& the_insn.reloc != SPARC_RELOC_LO10
&& the_insn.reloc != SPARC_RELOC_HI22
&& (the_insn.exp.X_add_number > immediate_max
|| the_insn.exp.X_add_number < ~immediate_max))
as_bad ("constant value must be between %ld and %ld",
~immediate_max, immediate_max);
if ((the_insn.reloc == SPARC_RELOC_WDISP22 ||
the_insn.reloc == SPARC_RELOC_WDISP30) &&
the_insn.exp.X_add_number & 3)
as_bad ("displacement is not long aligned");
/* plug absolutes directly into opcode */
switch(the_insn.reloc) {
case SPARC_RELOC_13:
if (the_insn.exp.X_seg == SEG_BIG)
opcode |= (*(int *) generic_bignum) & 0x1fff;
else
opcode |= the_insn.exp.X_add_number & 0x1fff;
the_insn.reloc = SPARC_RELOC_NONE;
break;
case SPARC_RELOC_22:
if (the_insn.exp.X_seg == SEG_BIG)
opcode |= (*(int *) generic_bignum) & 0x3fffff;
else
opcode |= the_insn.exp.X_add_number & 0x3fffff;
the_insn.reloc = SPARC_RELOC_NONE;
break;
case SPARC_RELOC_HI22:
/* extract upper 22 bits from constant */
opcode |= (the_insn.exp.X_add_number >> 10) & 0x3fffff;
the_insn.reloc = SPARC_RELOC_NONE;
break;
case SPARC_RELOC_LO10:
opcode |= the_insn.exp.X_add_number & 0x3ff;
break;
/* the PC relative displacements are plugged in
if the argument is absolute, but retain
relocatability */
case SPARC_RELOC_WDISP22:
opcode |= (the_insn.exp.X_add_number >> 2) & 0x3fffff;
break;
case SPARC_RELOC_WDISP30:
opcode |= (the_insn.exp.X_add_number >> 2) & 0x3fffffff;
break;
default:
printf("Unknown reloc entry\n");
}
}
/* Reset to prevent extraneous range check. */
immediate_max = 0;
continue;
case 'a':
if (*s++ == 'a')
{
opcode |= ANNUL;
continue;
}
break;
case 'A':
{
char *push = input_line_pointer;
expressionS e;
input_line_pointer = s;
expression (&e);
if (e.X_seg == SEG_ABSOLUTE)
{
opcode |= e.X_add_number << 5;
s = input_line_pointer;
input_line_pointer = push;
continue;
} /* if absolute */
break;
} /* alternate space */
case 'p':
if (strncmp (s, "%psr", 4) == 0)
{
s += 4;
continue;
}
break;
case 'q': /* floating point queue */
if (strncmp (s, "%fq", 3) == 0)
{
s += 3;
continue;
}
break;
case 'Q': /* coprocessor queue */
if (strncmp (s, "%cq", 3) == 0)
{
s += 3;
continue;
}
break;
case 'S':
if (strcmp (str, "set") == 0)
{
special_case_set = 1;
continue;
}
break;
case 't':
if (strncmp (s, "%tbr", 4) != 0)
break;
s += 4;
continue;
case 'w':
if (strncmp (s, "%wim", 4) != 0)
break;
s += 4;
continue;
case 'y':
if (strncmp (s, "%y", 2) != 0)
break;
s += 2;
continue;
default:
as_fatal ("failed sanity check.");
} /* switch on arg code */
break;
} /* for each arg that we expect */
error:
if (match == 0)
{
/* Args don't match. */
if (((unsigned) (&insn[1] - sparc_opcodes)) < NUMOPCODES
&& !strcmp (insn->name, insn[1].name))
{
++insn;
s = argsStart;
continue;
}
else
{
as_bad ("Illegal operands%s", error_message);
return;
}
}
else
{
if (insn->architecture > current_architecture)
{
if ((!architecture_requested || warn_on_bump)
&&
1
)
{
if (warn_on_bump)
{
as_warn ("architecture bumped from \"%s\" to \"%s\" on \"%s\"",
architecture_pname[current_architecture],
architecture_pname[insn->architecture],
str);
} /* if warning */
current_architecture = insn->architecture;
}
else
{
as_bad ("architecture mismatch on \"%s\" (\"%s\"). current architecture is \"%s\"",
str,
architecture_pname[insn->architecture],
architecture_pname[current_architecture]);
return;
} /* if bump ok else error */
} /* if architecture higher */
} /* if no match */
break;
} /* forever looking for a match */
the_insn.opcode = opcode;
}
static int
getExpression (str)
char *str;
{
char *save_in;
segT seg;
save_in = input_line_pointer;
input_line_pointer = str;
seg = expression (&the_insn.exp);
if (seg != SEG_ABSOLUTE
&& seg != SEG_SECT
&& seg != SEG_DIFFSECT
&& seg != SEG_UNKNOWN
&& seg != SEG_NONE
&& seg != SEG_BIG) {
the_insn.error = "bad segment";
expr_end = input_line_pointer;
input_line_pointer = save_in;
return 1;
}
expr_end = input_line_pointer;
input_line_pointer = save_in;
return 0;
} /* getExpression() */
/*
This is identical to the md_atof in m68k.c. I think this is right,
but I'm not sure.
Turn a string in input_line_pointer into a floating point constant of type
type, and store the appropriate bytes in *litP. The number of LITTLENUMS
emitted is stored in *sizeP . An error message is returned, or NULL on OK.
*/
/* Equal to MAX_PRECISION in atof-ieee.c */
#define MAX_LITTLENUMS 6
char *
md_atof (type, litP, sizeP)
char type;
char *litP;
int *sizeP;
{
int prec;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
LITTLENUM_TYPE *wordP;
char *t;
char *atof_ieee ();
switch (type)
{
case 'f':
case 'F':
case 's':
case 'S':
prec = 2;
break;
case 'd':
case 'D':
case 'r':
case 'R':
prec = 4;
break;
case 'x':
case 'X':
prec = 6;
break;
case 'p':
case 'P':
prec = 6;
break;
default:
*sizeP = 0;
return "Bad call to MD_ATOF()";
}
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
*sizeP = prec * sizeof (LITTLENUM_TYPE);
for (wordP = words; prec--;)
{
md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
litP += sizeof (LITTLENUM_TYPE);
}
return "";
}
/*
* Write out big-endian.
*/
void
md_number_to_chars (buf, val, n)
char *buf;
long val;
int n;
{
// sigh, all architectures do this..,
switch(n) {
case 4:
*buf++ = val >> 24;
*buf++ = val >> 16;
case 2:
*buf++ = val >> 8;
case 1:
*buf = val;
break;
default:
abort();
}
}
/* Apply a fixS to the frags, now that we know the value it ought to
hold. */
void
md_number_to_imm(unsigned char *buf, long val, int size, fixS *fixP, int nsect)
{
/* handle the most common case quickly */
if ((fixP->fx_r_type == NO_RELOC) ||
(fixP->fx_r_type == SPARC_RELOC_NONE) ||
(fixP->fx_r_type == SPARC_RELOC_VANILLA)) {
switch(size){
case 4:
*buf++ = val >> 24;
*buf++ = val >> 16;
case 2:
*buf++ = val >> 8;
case 1:
*buf = val;
break;
default:
abort();
}
return;
}
switch (fixP->fx_r_type) {
case SPARC_RELOC_WDISP30:
val = (val >> 2) + 1; /* adjust for word displacement */
buf[0] |= (val >> 24) & 0x3f;
buf[1] = (val >> 16);
buf[2] = val >> 8;
buf[3] = val;
break;
case SPARC_RELOC_WDISP22:
val = (val >>= 2) + 1;
buf[1] |= (val >> 16) & 0x3f;
buf[2] = val >> 8;
buf[3] = val;
break;
case SPARC_RELOC_HI22:
buf[1] |= (val >> 26) & 0x3f;
buf[2] = val >> 18;
buf[3] = val >> 10;
break;
case SPARC_RELOC_LO10:
buf[2] |= (val >> 8) & 0x03;
buf[3] = val;
break;
/* special cases that need to be handled internally by the as */
case SPARC_RELOC_22:
if (!fixP->fx_addsy) {
if (val & ~0x003fffff) {
as_bad ("relocation overflow");
} /* on overflow */
buf[1] |= (val >> 16) & 0x3f;
buf[2] = val >> 8;
buf[3] = val & 0xff;
} else
as_bad ("Undefined symbolic 22-bit immediate reference: %s",
fixP->fx_addsy->sy_name);
break;
case SPARC_RELOC_13:
if (!fixP->fx_addsy) {
if (((val > 0) && (val & ~(offsetT)0x00001fff))
|| ((val < 0) && (~(val - 1) & ~(offsetT)0x00001fff))) {
as_bad ("relocation overflow");
}
buf[2] |= (val >> 8) & 0x1f;
buf[3] = val;
} else
as_bad ("Undefined symbolic 13-bit immediate reference: %s",
fixP->fx_addsy->sy_name);
break;
case SPARC_RELOC_NONE:
default:
as_bad ("bad or unhandled relocation type: 0x%02x", fixP->fx_r_type);
break;
}
}
/*
* md_parse_option
* Invocation line includes a switch not recognized by the base assembler.
* See if it's a processor-specific option. These are:
*
* -bump
* Warn on architecture bumps. See also -A.
*
* -Av6, -Av7, -Av8, -Asparclite
* Select the architecture. Instructions or features not
* supported by the selected architecture cause fatal errors.
*
* The default is to start at v6, and bump the architecture up
* whenever an instruction is seen at a higher level.
*
* If -bump is specified, a warning is printing when bumping to
* higher levels.
*
* If an architecture is specified, all instructions must match
* that architecture. Any higher level instructions are flagged
* as errors.
*
* if both an architecture and -bump are specified, the
* architecture starts at the specified level, but bumps are
* warnings.
*
*/
int
md_parse_option (argP, cntP, vecP)
char **argP;
int *cntP;
char ***vecP;
{
char *p;
const char **arch;
if (!strcmp (*argP, "bump"))
{
warn_on_bump = 1;
}
else if (**argP == 'A')
{
p = (*argP) + 1;
for (arch = architecture_pname; *arch != NULL; ++arch)
{
if (strcmp (p, *arch) == 0)
{
break;
} /* found a match */
} /* walk the pname table */
if (*arch == NULL)
{
as_bad ("unknown architecture: %s", p);
}
else
{
current_architecture = (enum sparc_architecture) (arch - architecture_pname);
architecture_requested = 1;
}
}
#ifndef NeXT
#ifdef OBJ_ELF
else if (**argP == 'V')
{
print_version_id ();
}
else if (**argP == 'Q')
{
/* Qy - do emit .comment
Qn - do not emit .comment */
}
else if (**argP == 's')
{
/* use .stab instead of .stab.excl */
}
#endif
else if (strcmp (*argP, "sparc") == 0)
{
/* Ignore -sparc, used by SunOS make default .s.o rule. */
}
#endif /* NeXT */
else
{
/* Unknown option */
(*argP)++;
return 0;
}
**argP = '\0'; /* Done parsing this switch */
return 1;
} /* md_parse_option() */
int
md_estimate_size_before_relax(
fragS *fragP,
int segment_type)
{
as_fatal("internal error: Relaxation should never occur");
return(0);
}
void
md_convert_frag(
fragS *fragP)
{
as_fatal("internal error: Relaxation should never occur");
}
#ifdef DEBUGINSN
char *
S_GET_NAME(sym)
symbolS *sym;
{
return (sym->sy_nlist.n_un.n_name);
}
/* for debugging only */
static void
print_insn (insn)
struct sparc_it *insn;
{
const char *const Reloc[] = {
"VANILLA",
"PAIR",
"HI22",
"LO10",
"DISP22",
"PCREL",
"22",
"13",
"SECTDIFF",
"HI22_SECTDIFF",
"LO10_SECTDIFF",
"NONE",
"UNUSED"
};
if (insn->error)
fprintf (stderr, "ERROR: %s\n", insn->error);
fprintf (stderr, "opcode=0x%08x\n", (unsigned int)insn->opcode);
fprintf (stderr, "reloc = %s\n", Reloc[insn->reloc]);
fprintf (stderr, "X_add_number = 0x%x\n",
insn->exp.X_add_number);
if (insn->exp.X_add_symbol != NULL)
fprintf(stderr, "Add symbol: %s\n", S_GET_NAME(insn->exp.X_add_symbol));
if (insn->exp.X_subtract_symbol != NULL)
fprintf(stderr, "Subtract symbol: %s\n", S_GET_NAME(insn->exp.X_subtract_symbol));
}
#endif